// $Id: DomainEntity.cs 10 2008-02-29 15:58:02Z mcartoixa $
/*
 *  Copyright (C) 2007 NourY Solutions
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 3 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library. If not, see <http://www.gnu.org/licenses/>.
 *
 */
/*! \file
 * \brief Generic interfaces and implementation of Domain Models.
 *
 * \author $Author: mcartoixa $
 * \date $Date: 2008-02-29 16:58:02 +0100 (ven., 29 févr. 2008) $
 * \version $Revision: 10 $
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Security.Permissions;
using System.Reflection;
using System.Text;
using Salamanca.DataRules;
#if (!NET_CF)
using System.Runtime.Serialization;
#endif

namespace Salamanca.DataAccess
{



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Interface implemented by a <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" />.</summary>
    ///
    ///////////////////////////////////////////////////////////////////////////

    public interface IDomainEntity:
        ITransferable,
        IPersistent
    {



        /// <summary>Refreshes the internal data of the instance.</summary>
        void Refresh(bool lazyLoad);
        /// <summary>Loads the internal data of the instance.</summary>
        void Load(bool lazyLoad);

        /// <summary>Deletes the internal data of the instance.</summary>
        void Delete();
        /// <summary>Saves the internal data of the instance.</summary>
        void Save();



        /// <summary>Gets the primary key of the instance.</summary>
        /// <value>The primary key of the instance.</value>
        IPrimaryKey Id
        {
            get;
        }

        /// <summary>Indicates whether the instance data is loaded or not (in which case it is a ghost).</summary>
        bool IsLoaded
        {
            get;
        }
        /// <summary>Indicates whether the instance data has been modified.</summary>
        bool IsModified
        {
            get;
            set;
        }
        /// <summary>Indicates whether the instance has been persisted.</summary>
        bool IsNew
        {
            get;
        }

        /// <summary>Unused</summary>
        IDictionary Childs
        {
            get;
        }
        /// <summary>Unused</summary>
        IDomainEntity Parent
        {
            get;
        }

        /// <summary>Gets or sets the <include file="../../doc/Global.doc.xml" path="doc/template[@name='DataMapper']/*" />s
        /// associated to the <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" />.</summary>
        Collections.DataMapperCollection DataMappers
        {
            get;
            set;
        }

        /// <summary>Occurs when the instance is about to be refreshed (<see cref="Refresh" />).</summary>
        event EventHandler Refreshing;
        /// <summary>Occurs when the instance has been refreshed (<see cref="Refresh" />).</summary>
        event EventHandler Refreshed;
        /// <summary>Occurs when the instance has been modified.</summary>
        event EventHandler<DomainEntityModifiedEventArgs> Modified;
    }



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Arguments for a <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> modification event.</summary>
    ///
    ///////////////////////////////////////////////////////////////////////////

    public sealed class DomainEntityModifiedEventArgs:
        EventArgs
    {

        /// <summary>Constructor.</summary>
        public DomainEntityModifiedEventArgs(bool isModified)
        {
            _IsModified=isModified;
        }

        /// <summary>Indicates whether the instance data has been modified.</summary>
        public bool IsModified
        {
            get
            {
                return _IsModified;
            }
        }

        /// <summary>Indicates whether the instance data has been modified.</summary>
        private bool _IsModified;
    }



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Ancestor of a <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" />.</summary>
    /// <remarks>
    /// <para>When a new instance of a <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> is created,
    /// it is assigned a temporary primary key. When the instance is <see cref="Save()">saved</see>,
    /// a definitive primary key is assigned to the instance. As the instance identity (cf. <see cref="GetHashCode()" /> and <see cref="Equals(object)" />)
    /// is based on the instance primary key, the following rule should be enforced :
    /// <note>A new <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> instance should not be used in contexts that rely on the
    /// value returned by <see cref="GetHashCode()" /> (such as keys in <see cref="IDictionary" /> implementations).</note>
    /// A notable exception to this rule being <see cref="Collections.DomainEntityKeyedCollection{TDomainEntity}" />, that can handle new 
    /// <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> instances.</para>
    /// <codeReference>QuickStart#DataMapper</codeReference>
    /// </remarks>
    /// <example>
    ///   <para>This sample defines a <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> called <c>Person</c>.
    /// The link to a data backend is made through the definition of <include file="../../doc/Global.doc.xml" path="doc/template[@name='DataMapper']/*" />s.
    /// Very likely, the backend is a database ; more precisely a table called <c>Persons</c> which could be defined like this :
    ///     <code>
    /// CREATE TABLE Persons (
    ///     Id INT NOT NULL IDENTITY(1, 1),
    ///     Name VARCHAR(50) NOT NULL,
    ///     PRIMARY KEY (Id)
    /// );
    ///     </code>
    ///   </para>
    ///   <para>The first step is to define classes for the primary key and the <include file="../../doc/Global.doc.xml" path="doc/template[@name='DataTransferObject']/*" /> :
    ///     <code>
    /// // Defines the primary key.
    /// public class PersonPrimaryKey:
    ///     PrimaryKey&lt;int&gt;
    /// {
    ///     public PrimaryKey():
    ///         base()
    ///     {
    ///     }
    ///
    ///     public PrimaryKey(int key):
    ///         base(key)
    ///     {
    ///     }
    ///
    ///     public static implicit operator Int32PrimaryKey(int key)
    ///     {
    ///         return new Int32PrimaryKey(key);
    ///     }
    ///
    ///     public static explicit operator int(Int32PrimaryKey key)
    ///     {
    ///         return key.Value;
    ///     }
    /// }
    ///
    ///
    /// // Defines the data transfer object.
    /// public partial class PersonDataTransfer:
    ///     IDataTransferObject
    /// {
    ///     public object Clone()
    ///     {
    ///         return MemberwiseClone();
    ///     }
    ///
    ///     public int Id;
    ///     public string Name;
    /// }
    ///     </code>
    ///   </para>
    ///   <para>The <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> can then be defined</para>
    ///   <code>
    /// // Defines an interface for a data mapper.
    /// public interface IPersonMapper:
    ///     IDataMapper
    /// {
    ///     Person Find(PersonPrimaryKey key, DataMapperCollection dataMappers);
    ///     DomainEntityKeyedCollection&lt;Person&gt; FindAll(DataMapperCollection dataMappers);
    /// }
    ///
    /// // Defines the domain model.
    /// public class Person:
    ///     DomainEntity&lt;PersonPrimaryKey, PersonDataTransfer&gt;
    /// {
    ///
    ///     public Person():
    ///         base()
    ///     {
    ///     }
    /// 
    ///     public Person(DataMapperCollection dataMappers):
    ///         base(dataMappers)
    ///     {
    ///     }
    /// 
    ///     protected override PersonPrimaryKey CreateId()
    ///     {
    ///         return new PersonPrimaryKey(Data.Id);
    ///     }
    ///
    ///     public static Person Find(PersonPrimaryKey key, DataMapperCollection dataMappers)
    ///     {
    ///         return ((IPersonMapper)dataMappers[typeof(Person)]).Find(key, dataMappers);
    ///     }
    ///
    ///     public static IList&lt;Person&gt; FindAll(DataMapperCollection dataMappers)
    ///     {
    ///         return ((IPersonMapper)dataMappers[typeof(Person)]).FindAll(dataMappers);
    ///     }
    ///
    ///     [StringLengthRule(50)]
    ///     [NotNullRule]
    ///     public string Name
    ///     {
    ///         get
    ///         {
    ///             Load(true);
    ///             return Data.Name;
    ///         }
    ///         set
    ///         {
    ///             if (Data.Name!=value)
    ///             {
    ///                 Data.Name=value;
    ///                 RaisePropertyChanged("Name");
    ///             }
    ///         }
    ///     }
    /// }
    ///   </code>
    /// </example>
    /// <typeparam name="TDataTransferObject">The type of the <include file="../../doc/Global.doc.xml" path="doc/template[@name='DataTransferObject']/*" />.</typeparam>
    /// <typeparam name="TPrimaryKey">The type of the primary key.</typeparam>
    ///
    ///////////////////////////////////////////////////////////////////////////

#if (!NET_CF)
    [Serializable]
#endif
    public abstract class DomainEntity<TPrimaryKey, TDataTransferObject>:
        IDomainEntity,
#if (!NET_CF)
        ISerializable,
#endif
        IBackupable,
        IDataErrorInfo,
        IEditableObject,
        INotifyPropertyChanged,
        IValidationExtensible
        where TPrimaryKey:
            IPrimaryKey,
            new()
        where TDataTransferObject:
            IDataTransferObject,
            new()
    {



        /// <summary>Constructor.</summary>
        protected DomainEntity():
            this(new TDataTransferObject())
        {
        }

        /// <summary>Constructor.</summary>
        protected DomainEntity(TDataTransferObject data)
        {
            Data=data;
        }

        /// <summary>Constructor.</summary>
        protected DomainEntity(Collections.DataMapperCollection dataMappers):
            this()
        {
            SetDataMappers(dataMappers);
        }

#if (!NET_CF)
        /// <summary>Deserialization constructor.</summary>
        protected DomainEntity(SerializationInfo info, StreamingContext context)
        {
            SetDataMappers((Collections.DataMapperCollection)info.GetValue("Mappers", typeof(Collections.DataMapperCollection)));

            IDomainEntityDataTransferObject dto=(IDomainEntityDataTransferObject)info.GetValue("Data", typeof(IDomainEntityDataTransferObject));
            DataMappers[GetType()].SetData(this, dto);
        }
#endif



        /// <summary>Serves as a hash function for this particular type.</summary>
        public override int GetHashCode()
        {
            return Id.GetHashCode();
        }

        /// <summary>Determines whether the specified <see cref="object" /> is equal to the current <see cref="DomainEntity{TPrimaryKey,TDataTransferObject}" />.</summary>
        public override bool Equals(object obj)
        {
            return (base.Equals(obj) || ((obj!=null) && (GetType()==obj.GetType())) && (((IDomainEntity)this).Id.Equals(((IDomainEntity)obj).Id)));
        }

        /// <summary>Equality operator.</summary>
        public static bool operator==(DomainEntity<TPrimaryKey, TDataTransferObject> a, DomainEntity<TPrimaryKey, TDataTransferObject> b)
        {
            if (object.ReferenceEquals(a, b))
                return true;

            // If one is null, but not both, return false.
            if (((object)a==null)||((object)b==null))
                return false;

            return a.Equals(b);
        }

        /// <summary>Inequality operator.</summary>
        public static bool operator!=(DomainEntity<TPrimaryKey, TDataTransferObject> a, DomainEntity<TPrimaryKey, TDataTransferObject> b)
        {
            return !(a==b);
        }



#if (!NET_CF)
        [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter)]
        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("Mappers", DataMappers);

            IDomainEntityDataTransferObject dto=DataMappers[GetType()].GetData(this, true);
            info.AddValue("Data", dto);
        }
#endif



        /// <summary>Creates the business rules used to <see cref="Validate()"/> the <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" />. </summary>
        /// <returns>The list of business rules created.</returns>
        protected virtual IList<IRule> CreateRules()
        {
            return DataRules.Rules.CreateRules(GetType());
        }



        /// <summary>Refreshes the internal data of the instance.</summary>
        public void Refresh(bool lazyLoad)
        {
            Unload();
            Load(lazyLoad);
        }

        /// <summary>Raises the <see cref="Refreshing" /> event.</summary>
        protected virtual void OnRefreshing(EventArgs e)
        {
            _IsLoaded=false;

            if (Refreshing!=null)
                Refreshing(this, e);
        }

        internal void TriggerRefreshingEvent(EventArgs e)
        {
            OnRefreshing(e);
        }

        /// <summary>Raises the <see cref="Refreshed" /> event.</summary>
        protected virtual void OnRefreshed(EventArgs e)
        {
            _IsLoaded=true;

            if (Refreshed!=null)
                Refreshed(this, e);
        }

        internal void TriggerRefreshedEvent(EventArgs e)
        {
            OnRefreshed(e);
        }



        /// <summary>Loads the data related to this instance.</summary>
        public virtual void Load(bool lazyLoad)
        {
            if ((!_IsNew) && (!_IsLoaded))
                DataMappers[GetType()].Refresh(this);
        }

        /// <summary>Unload the data related to this instance.</summary>
        public virtual void Unload()
        {
            _IsLoaded=false;
        }

        internal void SetLoaded(bool isLoaded)
        {
            _IsLoaded=isLoaded;
        }



        /// <summary>Indicates that a property has changed.</summary>
        protected void RaisePropertyChanged(string propertyName)
        {
            RaisePropertyChanged(propertyName, true);
        }

        /// <summary>Indicates that a property has changed.</summary>
        protected void RaisePropertyChanged(string propertyName, bool isModified)
        {
            OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
            SetModified(isModified);
        }

        /// <summary>Raises the <see cref="PropertyChanged" /> event.</summary>
        /// <remarks>
        ///   <include file="../../doc/Global.doc.xml" path="doc/template[@name='OnEvent']/*" />
        /// </remarks>
        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (PropertyChanged!=null)
                PropertyChanged(this, e);
        }



        /// <summary>Saves the internal data of the instance.</summary>
        /// <remarks>A new <see cref="IPersistenceContext" /> is created for this operation, which is likely to make it atomic.
        /// If you want to make a batch of several operations atomic, create your own  <see cref="IPersistenceContext" /> and use <see cref="Save(IPersistenceContext)" /> instead.</remarks>
        public void Save()
        {
            using (IPersistenceContext ctx=DataMappers.CreatePersistenceContext())
            {
                Save(ctx);
                ctx.Commit();
            }
        }

        /// <summary>Saves the internal data of the instance.</summary>
        /// <remarks>A new <see cref="IPersistenceContext" /> is created for this operation, which is likely to make it atomic.
        /// If you want to make a batch of several operations atomic, create your own  <see cref="IPersistenceContext" /> and use <see cref="Save(IPersistenceContext)" /> instead.</remarks>
        public void Save(bool force)
        {
            using (IPersistenceContext ctx=DataMappers.CreatePersistenceContext(force))
            {
                Save(ctx);
                ctx.Commit();
            }
        }

        /// <summary>Saves the internal data of the instance as part of the specified context.</summary>
        public void Save(IPersistenceContext context)
        {
            Debug.Assert(context!=null);
            if (context==null)
                throw new ArgumentNullException("context");

            if (_SaveLock==null || !_SaveLock.Locked)
                using (_SaveLock=new PersistenceLock())
                    ((IDataMapper)DataMappers[GetType()]).Save(this, context);
        }

        /// <summary>Raises the <see cref="Saving" /> event.</summary>
        /// <remarks>
        ///   <include file="../../doc/Global.doc.xml" path="doc/template[@name='OnEvent']/*" />
        /// </remarks>
        protected virtual void OnSaving(PersistenceEventArgs e)
        {
            if (Saving!=null)
                Saving(this, e);
        }

        internal void TriggerSavingEvent(PersistenceEventArgs e)
        {
            OnSaving(e);
        }

        /// <summary>Raises the <see cref="Saved" /> event.</summary>
        /// <remarks>
        ///   <include file="../../doc/Global.doc.xml" path="doc/template[@name='OnEvent']/*" />
        /// </remarks>
        protected virtual void OnSaved(PersistenceEventArgs e)
        {
            if (Saved!=null)
                Saved(this, e);
        }

        internal void TriggerSavedEvent(PersistenceEventArgs e)
        {
            OnSaved(e);

            _TemporaryId=null;
            GC.KeepAlive(InternalId); // Trick to recreate the internal Id
        }



        /// <summary>Deletes the internal data of the instance.</summary>
        /// <remarks>A new <see cref="IPersistenceContext" /> is created for this operation, which is likely to make it atomic.
        /// If you want to make a batch of several operations atomic, create your own  <see cref="IPersistenceContext" /> and use <see cref="Delete(IPersistenceContext)" /> instead.</remarks>
        public void Delete()
        {
            using (IPersistenceContext ctx=DataMappers.CreatePersistenceContext())
            {
                Delete(ctx);
                ctx.Commit();
            }
        }

        /// <summary>Deletes the internal data of the instance.</summary>
        /// <remarks>A new <see cref="IPersistenceContext" /> is created for this operation, which is likely to make it atomic.
        /// If you want to make a batch of several operations atomic, create your own  <see cref="IPersistenceContext" /> and use <see cref="Delete(IPersistenceContext)" /> instead.</remarks>
        public void Delete(bool force)
        {
            using (IPersistenceContext ctx=DataMappers.CreatePersistenceContext(force))
            {
                Delete(ctx);
                ctx.Commit();
            }
        }

        /// <summary>Deletes the internal data of the instance.</summary>
        public void Delete(IPersistenceContext context)
        {
            Debug.Assert(context!=null);
            if (context==null)
                throw new ArgumentNullException("context");

            if (_DeleteLock==null || !_DeleteLock.Locked)
                using (_DeleteLock=new PersistenceLock())
                    ((IDataMapper)DataMappers[GetType()]).Delete(this, context);
        }

        /// <summary>Raises the <see cref="Deleting" /> event.</summary>
        /// <remarks>
        ///   <include file="../../doc/Global.doc.xml" path="doc/template[@name='OnEvent']/*" />
        /// </remarks>
        protected virtual void OnDeleting(PersistenceEventArgs e)
        {
            if (Deleting!=null)
                Deleting(this, e);
        }

        internal void TriggerDeletingEvent(PersistenceEventArgs e)
        {
            OnDeleting(e);
        }

        /// <summary>Raises the <see cref="Deleted" /> event.</summary>
        /// <remarks>
        ///   <include file="../../doc/Global.doc.xml" path="doc/template[@name='OnEvent']/*" />
        /// </remarks>
        protected virtual void OnDeleted(PersistenceEventArgs e)
        {
            if (Deleted!=null)
                Deleted(this, e);
        }

        internal void TriggerDeletedEvent(PersistenceEventArgs e)
        {
            OnDeleted(e);
        }



        /// <summary>Raises the <see cref="Modified" /> event.</summary>
        /// <remarks>
        ///   <include file="../../doc/Global.doc.xml" path="doc/template[@name='OnEvent']/*" />
        /// </remarks>
        protected virtual void OnModified(DomainEntityModifiedEventArgs e)
        {
            if (Modified!=null)
                Modified(this, e);
        }

        /// <summary>Indicates whether the instance data has been modified.</summary>
        public bool IsModified
        {
            get
            {
                return (_IsNew || _IsModified);
            }
            protected set
            {
                SetModified(_IsModified | value);
            }
        }

        /// <summary>Indicates whether the instance data has been modified.</summary>
        bool IDomainEntity.IsModified
        {
            get
            {
                return IsModified;
            }
            set
            {
                IsModified=value;
            }
        }

        internal void SetModified(bool isModified)
        {
            if (_IsModified!=isModified)
            {
                _IsModified=isModified;
                OnPropertyChanged(new PropertyChangedEventArgs("IsModified"));
            }

            OnModified(new DomainEntityModifiedEventArgs(IsModified));
        }



        /// <summary>Indicates whether the instance has been persisted.</summary>
        protected bool IsNew
        {
            get
            {
                return _IsNew;
            }
        }

        /// <summary>Indicates whether the instance has been persisted.</summary>
        bool IDomainEntity.IsNew
        {
            get
            {
                return IsNew;
            }
        }

        internal void SetNew(bool isNew)
        {
            _IsNew=isNew;

            OnModified(new DomainEntityModifiedEventArgs(IsModified));
        }

        

        /// <summary>Indicates whether the instance data is loaded or not (in which case it is a ghost).</summary>
        bool IDomainEntity.IsLoaded
        {
            get
            {
                return IsLoaded;
            }
        }

        /// <summary>Indicates whether the instance data is loaded or not (in which case it is a ghost).</summary>
        protected bool IsLoaded
        {
            get
            {
                return _IsLoaded;
            }
        }



        /// <summary>Creates a primary key for the instance.</summary>
        protected abstract TPrimaryKey CreateId();

        /// <summary>Gets the primary key of the instance.</summary>
        /// <value>The primary key of the instance.</value>
        protected TPrimaryKey Id
        {
            get
            {
                return InternalId;
            }
        }

        /// <summary>Gets the primary key of the instance.</summary>
        /// <value>The primary key of the instance.</value>
        IPrimaryKey IDomainEntity.Id
        {
            get
            {
                return Id;
            }
        }

        /// <summary>Gets the primary key of the instance.</summary>
        /// <value>The primary key of the instance.</value>
        internal protected virtual TPrimaryKey InternalId
        {
            get
            {
                if (IsNew)
                    return TemporaryId;

                if (_Id==null)
                    _Id=CreateId();

                return (TPrimaryKey)_Id;
            }
        }

        private TPrimaryKey TemporaryId
        {
            get
            {
                if (_TemporaryId==null)
                    _TemporaryId=PrimaryKey.CreateTemporary<TPrimaryKey>();
                return (TPrimaryKey)_TemporaryId;
            }
        }



        private void SetDataMappers(Collections.DataMapperCollection dataMappers)
        {
            if (dataMappers==null)
                throw new ArgumentNullException("dataMappers");
            if (!dataMappers.ContainsKey(GetType()))
                throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.SR.NoDataMapperForType, GetType()), "dataMappers");

            _DataMappers=dataMappers;
        }

        /// <summary>Gets or sets the <include file="../../doc/Global.doc.xml" path="doc/template[@name='DataMapper']/*" />s
        /// associated to the <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> instance.</summary>
        Collections.DataMapperCollection IDomainEntity.DataMappers
        {
            get
            {
                return _DataMappers;
            }
            set
            {
                SetDataMappers(value);
            }
        }

        /// <summary>Gets or sets the <include file="../../doc/Global.doc.xml" path="doc/template[@name='DataMapper']/*" /> associated with the instance.</summary>
        /// <value>The <include file="../../doc/Global.doc.xml" path="doc/template[@name='DataMapper']/*" /> associated with the instance.</value>
        protected Collections.DataMapperCollection DataMappers
        {
            get
            {
                if (_DataMappers==null)
                    throw new InvalidOperationException(Resources.SR.NoDataMapperFound);

                return _DataMappers;
            }
        }



        /// <summary>Gets the <include file="../../doc/Global.doc.xml" path="doc/template[@name='DataTransferObject']/*" />
        /// filled with the current instance data.</summary>
        /// <summary>The <include file="../../doc/Global.doc.xml" path="doc/template[@name='DataTransferObject']/*" />
        /// filled with the current instance data.</summary>
        IDataTransferObject ITransferable.GetData()
        {
            return GetData();
        }

        /// <summary>Gets the <include file="../../doc/Global.doc.xml" path="doc/template[@name='DataTransferObject']/*" />
        /// filled with the current instance data.</summary>
        /// <returns>The <include file="../../doc/Global.doc.xml" path="doc/template[@name='DataTransferObject']/*" />
        /// filled with the current instance data.</returns>
        protected virtual TDataTransferObject GetData()
        {
            return Data;
        }

        /// <summary>Assigns the specified data to the current instance.</summary>
        /// <param name="data">The <include file="../../doc/Global.doc.xml" path="doc/template[@name='DataTransferObject']/*" /> containing the data.</param>
        void ITransferable.SetData(IDataTransferObject data)
        {
            SetData((TDataTransferObject)data);
        }

        /// <summary>Assigns the specified data to the current instance.</summary>
        /// <param name="data">The <include file="../../doc/Global.doc.xml" path="doc/template[@name='DataTransferObject']/*" /> containing the data.</param>
        protected virtual void SetData(TDataTransferObject data)
        {
            Data=data;
        }



        IDictionary IDomainEntity.Childs
        {
            get
            {
                return Childs;
            }
        }

        /// <summary>Unused</summary>
        protected virtual IDictionary Childs
        {
            get
            {
                return _Childs;
            }
        }

        IDomainEntity IDomainEntity.Parent
        {
            get
            {
                return Parent;
            }
        }

        /// <summary>Unused</summary>
        protected virtual IDomainEntity Parent
        {
            get
            {
                return null;
            }
        }



        #region IBackupable
        /// <summary>Creates a backup of the current instance.</summary>
        protected virtual IDomainEntityDataTransferObject CreateBackup()
        {
            return (IDomainEntityDataTransferObject)DataMappers[GetType()].GetData(this, true).Clone();
        }

        /// <summary>Restores a backup of the current instance.</summary>
        protected virtual void RestoreBackup(IDomainEntityDataTransferObject backup)
        {
            bool wasLoaded=IsLoaded;
            Unload();

            DataMappers[GetType()].SetData(this, backup);

            if (wasLoaded)
                Load(true);
        }

        /// <summary>Creates a backup of the current instance.</summary>
        object IBackupable.CreateBackup()
        {
            return CreateBackup();
        }

        /// <summary>Restores a backup of the current instance.</summary>
        void IBackupable.RestoreBackup(object backup)
        {
            RestoreBackup((IDomainEntityDataTransferObject)backup);
        }
        #endregion



        #region IEditableObject
        /// <summary>Begins an edit on the instance.</summary>
        protected virtual void BeginEdit()
        {
            if (_BackupData==null)
                _BackupData=CreateBackup();
        }

        /// <summary>Discards changes since the first <see cref="BeginEdit()" /> call.</summary>
        protected virtual void CancelEdit()
        {
            if (_BackupData!=null)
            {
                RestoreBackup(_BackupData);
                _BackupData=null;
            }
        }

        /// <summary>Pushes changes since the last <see cref="BeginEdit()" /> call into the underlying object.</summary>
        protected virtual void EndEdit()
        {
            _BackupData=null;
        }

        /// <summary>Begins an edit on the instance.</summary>
        void IEditableObject.BeginEdit()
        {
            BeginEdit();
        }

        /// <summary>Discards changes since the last <see cref="BeginEdit()" /> call.</summary>
        void IEditableObject.CancelEdit()
        {
            CancelEdit();
        }

        /// <summary>Pushes changes since the last <see cref="BeginEdit()" /> call into the underlying object.</summary>
        void IEditableObject.EndEdit()
        {
            EndEdit();
        }
        #endregion



        #region IDataErrorInfo
        /// <summary>Gets an error message indicating what is wrong with this object.</summary>
        string IDataErrorInfo.Error
        {
            get
            {
                string ret=((IDataErrorInfo)this)[string.Empty];

                return ret ?? string.Empty;
            }
        }

        /// <summary>Gets the error message for the property with the given name.</summary>
        /// <param name="propertyName">The name of the property whose error message to get.</param>
        /// <returns>The error message for the property. The default is an empty string ("").</returns>
        string IDataErrorInfo.this[string propertyName]
        {
            get
            {
                return DataRules.Rules.GetMessage(GetBrokenRules(propertyName));
            }
        }
        #endregion



        #region IValidatable
        /// <summary>Validates the <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> data.</summary>
        /// <returns><c>true</c> if the instance data is valid.</returns>
        public bool Validate()
        {
            return Validate(null);
        }

        /// <summary>Validates the <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> data.</summary>
        /// <returns><c>true</c> if the instance data is valid.</returns>
        public bool Validate(string propertyName)
        {
            OnValidating(new ValidationEventArgs(propertyName));

            return DataRules.Rules.Validate(this, propertyName, Rules);
        }

        /// <summary>Gets the complete list of broken rules of the current instance.</summary>
        /// <returns>The list of broken rules for this instance.</returns>
        private ReadOnlyCollection<IRule> GetBrokenRules()
        {
            return GetBrokenRules(null);
        }


        /// <summary>Gets the list of broken rules concerning the specified property of the current instance.</summary>
        /// <returns>The list of broken rules concerning the specified property for this instance.</returns>
        private ReadOnlyCollection<IRule> GetBrokenRules(string propertyName)
        {
            OnValidating(new ValidationEventArgs(propertyName));

            return DataRules.Rules.GetBrokenRules(this, propertyName, Rules);
        }

        /// <summary>Gets the complete list of broken rules of the current instance.</summary>
        /// <returns>The list of broken rules for this instance.</returns>
        IList<IRule> IValidatable.GetBrokenRules()
        {
            return GetBrokenRules();
        }


        /// <summary>Gets the list of broken rules concerning the specified property of the current instance.</summary>
        /// <returns>The list of broken rules concerning the specified property for this instance.</returns>
        IList<IRule> IValidatable.GetBrokenRules(string propertyName)
        {
            return GetBrokenRules(propertyName);
        }

        /// <summary>Raises the <see cref="Validating" /> event.</summary>
        /// <remarks>
        ///   <include file="../../doc/Global.doc.xml" path="doc/template[@name='OnEvent']/*" />
        /// </remarks>
        protected virtual void OnValidating(ValidationEventArgs e)
        {
            if (Validating!=null)
                Validating(this, e);
        }

        /// <summary>The complete list of the validation rules of this instance.</summary>
        private IList<IRule> Rules
        {
            get
            {
                // Get the list of intrinsic rules
                if (_IntrinsicRules==null)
                {
                    _ExtendedRules=null;

                    // They are the same for every instance of this type
                    if (!_RulesMap.TryGetValue(GetType(), out _IntrinsicRules))
                    {
                        IList<IRule> tmp=CreateRules();

                        // Validate ... the rules !
                        foreach (IRule r in tmp)
                        {
                            // The property name must be valid for this type
                            bool check=
                                (r.PropertyName.Length==0)||
                                (GetType().GetProperty(r.PropertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy)!=null);
                            Debug.Assert(check);
                            if (!check)
                                throw new InvalidOperationException();
                        }

                        _IntrinsicRules=tmp;
                        _RulesMap.Add(GetType(), _IntrinsicRules);
                    }
                }

                // Add the extended rules to this predefined set
                if (_ExtendedRules==null)
                {
                    _ExtendedRules=new List<IRule>(_IntrinsicRules);
                    foreach (IValidationExtenderProvider provider in _ValidationExtenderProviders)
                        _ExtendedRules.AddRange(provider.GetExtendedRules(this));
                }

                return _ExtendedRules;
            }
        }

        /// <summary>The complete list of the validation rules of this instance.</summary>
        IList<IRule> IValidatable.Rules
        {
            get
            {
                return Rules;
            }
        }
        #endregion



        #region IValidationExtensible
        /// <summary>Registers the specified extender on the current instance.</summary>
        /// <param name="provider">The validation extender provider.</param>
        /// <returns><c>true</c> if the extender provider was succesfully registered.</returns>
        bool IValidationExtensible.RegisterExtender(IValidationExtenderProvider provider)
        {
            if (_ValidationExtenderProviders.Contains(provider))
                throw new InvalidOperationException(Resources.SR.AlreadyRegisteredProvider);

            _ExtendedRules=null;
            _ValidationExtenderProviders.Add(provider);
            return true;
        }

        /// <summary>Unregisters the specified extender on the current instance.</summary>
        /// <param name="provider">The validation extender provider.</param>
        void IValidationExtensible.UnregisterExtender(IValidationExtenderProvider provider)
        {
            _ExtendedRules=null;
            _ValidationExtenderProviders.Remove(provider);
        }
        #endregion



        /// <summary>Occurs when the instance is about to be refreshed (<see cref="Refresh" />).</summary>
        public event EventHandler Refreshing;
        /// <summary>Occurs when the instance has been refreshed (<see cref="Refresh" />).</summary>
        public event EventHandler Refreshed;
        /// <summary>Occurs when the instance is about to be deleted (<see cref="Delete()" />).</summary>
        public event EventHandler<PersistenceEventArgs> Deleting;
        /// <summary>Occurs when the instance has been deleted (<see cref="Delete()" />).</summary>
        public event EventHandler<PersistenceEventArgs> Deleted;
        /// <summary>Occurs when the instance is about to be saved (<see cref="Save()" />).</summary>
        public event EventHandler<PersistenceEventArgs> Saving;
        /// <summary>Occurs when the instance has been saved (<see cref="Save()" />).</summary>
        public event EventHandler<PersistenceEventArgs> Saved;
        /// <summary>Occurs when a property value changes.</summary>
        public event PropertyChangedEventHandler PropertyChanged;
        /// <summary>Occurs when the instance has been modified.</summary>
        public event EventHandler<DomainEntityModifiedEventArgs> Modified;
        /// <summary>Occurs when the instance is about to be validated.</summary>
        public event EventHandler<ValidationEventArgs> Validating;


        /// <summary>Indicates whether the instance data is loaded or not (in which case it is a ghost).</summary>
        private bool _IsLoaded;
        private HybridDictionary _Childs=new HybridDictionary(0);
        /// <summary>The primary key of this instance.</summary>
        private object _Id;
        private object _TemporaryId;
        /// <summary>The collection of <include file="../../doc/Global.doc.xml" path="doc/template[@name='DataMapper']/*" />s associated with the instance.</summary>
        private Collections.DataMapperCollection _DataMappers;
        /// <summary>Internal data.</summary>
        protected TDataTransferObject Data;
        /// <summary>Backup of the internal <see cref="Data" />.</summary>
        private IDomainEntityDataTransferObject _BackupData;
        /// <summary>The list of the instrinsic validation rules of this instance.</summary>
        private IList<IRule> _IntrinsicRules;
        /// <summary>The complete list of the validation rules of this instance.</summary>
        private List<IRule> _ExtendedRules;
        /// <summary>The list of the registered validation rules extender providers.</summary>
        private List<IValidationExtenderProvider> _ValidationExtenderProviders=new List<IValidationExtenderProvider>(0);
        /// <summary>Indicates whether the instance data has been modified.</summary>
        private bool _IsModified=true;
        /// <summary>Indicates whether the instance has been persisted or not.</summary>
        private bool _IsNew=true;
        /// <summary>Persistence lock used to avoid reentrance.</summary>
        private PersistenceLock _SaveLock;
        /// <summary>Persistence lock used to avoid reentrance.</summary>
        private PersistenceLock _DeleteLock;

        /// <summary>Cache of already defined intrinsic validation rules.</summary>
        private static IdentityMap<Type, IList<IRule>> _RulesMap=new IdentityMap<Type, IList<IRule>>();
    }

}
